{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Computer Vision Nanodegree\n",
    "\n",
    "## Project: Image Captioning\n",
    "\n",
    "---\n",
    "\n",
    "In this notebook, you will train your CNN-RNN model.  \n",
    "\n",
    "You are welcome and encouraged to try out many different architectures and hyperparameters when searching for a good model.\n",
    "\n",
    "This does have the potential to make the project quite messy!  Before submitting your project, make sure that you clean up:\n",
    "- the code you write in this notebook.  The notebook should describe how to train a single CNN-RNN architecture, corresponding to your final choice of hyperparameters.  You should structure the notebook so that the reviewer can replicate your results by running the code in this notebook.  \n",
    "- the output of the code cell in **Step 2**.  The output should show the output obtained when training the model from scratch.\n",
    "\n",
    "This notebook **will be graded**.  \n",
    "\n",
    "Feel free to use the links below to navigate the notebook:\n",
    "- [Step 1](#step1): Training Setup\n",
    "- [Step 2](#step2): Train your Model\n",
    "- [Step 3](#step3): (Optional) Validate your Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='step1'></a>\n",
    "## Step 1: Training Setup\n",
    "\n",
    "In this step of the notebook, you will customize the training of your CNN-RNN model by specifying hyperparameters and setting other options that are important to the training procedure.  The values you set now will be used when training your model in **Step 2** below.\n",
    "\n",
    "You should only amend blocks of code that are preceded by a `TODO` statement.  **Any code blocks that are not preceded by a `TODO` statement should not be modified**.\n",
    "\n",
    "### Task #1\n",
    "\n",
    "Begin by setting the following variables:\n",
    "- `batch_size` - the batch size of each training batch.  It is the number of image-caption pairs used to amend the model weights in each training step. \n",
    "- `vocab_threshold` - the minimum word count threshold.  Note that a larger threshold will result in a smaller vocabulary, whereas a smaller threshold will include rarer words and result in a larger vocabulary.  \n",
    "- `vocab_from_file` - a Boolean that decides whether to load the vocabulary from file. \n",
    "- `embed_size` - the dimensionality of the image and word embeddings.  \n",
    "- `hidden_size` - the number of features in the hidden state of the RNN decoder.  \n",
    "- `num_epochs` - the number of epochs to train the model.  We recommend that you set `num_epochs=3`, but feel free to increase or decrease this number as you wish.  [This paper](https://arxiv.org/pdf/1502.03044.pdf) trained a captioning model on a single state-of-the-art GPU for 3 days, but you'll soon see that you can get reasonable results in a matter of a few hours!  (_But of course, if you want your model to compete with current research, you will have to train for much longer._)\n",
    "- `save_every` - determines how often to save the model weights.  We recommend that you set `save_every=1`, to save the model weights after each epoch.  This way, after the `i`th epoch, the encoder and decoder weights will be saved in the `models/` folder as `encoder-i.pkl` and `decoder-i.pkl`, respectively.\n",
    "- `print_every` - determines how often to print the batch loss to the Jupyter notebook while training.  Note that you **will not** observe a monotonic decrease in the loss function while training - this is perfectly fine and completely expected!  You are encouraged to keep this at its default value of `100` to avoid clogging the notebook, but feel free to change it.\n",
    "- `log_file` - the name of the text file containing - for every step - how the loss and perplexity evolved during training.\n",
    "\n",
    "If you're not sure where to begin to set some of the values above, you can peruse [this paper](https://arxiv.org/pdf/1502.03044.pdf) and [this paper](https://arxiv.org/pdf/1411.4555.pdf) for useful guidance!  **To avoid spending too long on this notebook**, you are encouraged to consult these suggested research papers to obtain a strong initial guess for which hyperparameters are likely to work best.  Then, train a single model, and proceed to the next notebook (**3_Inference.ipynb**).  If you are unhappy with your performance, you can return to this notebook to tweak the hyperparameters (and/or the architecture in **model.py**) and re-train your model.\n",
    "\n",
    "### Question 1\n",
    "\n",
    "**Question:** Describe your CNN-RNN architecture in detail.  With this architecture in mind, how did you select the values of the variables in Task 1?  If you consulted a research paper detailing a successful implementation of an image captioning model, please provide the reference.\n",
    "\n",
    "**Answer:**                                        **Model.py**\n",
    "As the architecture's Encoder CNN is already pretrained on Resnet50 which is provided by Udacity itself, So I haven't tweaked the class EncoderCNN. While in DecoderRNN I have intitialized embed_size, hidden_size, vocab_size, number of layers having initialized super function to gain access to inherited methods also initialized the embedding layer. Next, I initialized LSTM layer with input size and other parameters like hidden_size, embed size, number of layers and batch first set to True. After that the output of the lstm layer is used in the fully connected linear layer having hidden size(512) and vocab size(9955) as it's parameters.In forward function I linked the feature image tensors with the embeddings and lastly, running them through the LSTM and then through the linear layer in sample function having initialized an empty list for output sentence which is to be appended with the result with predicted words with maximum probabilities\n",
    "\n",
    "**Task 1** I intuitively selected the batch size as 64 since it's in the middle of 32,64,128. apart from that the choice of values for vocab threshold, hidden size, embeddings are based on knowledge from various research paper, blogs and Udacity's content with vocab_from_file set to true.\n",
    "\n",
    "**Research Paper for Reference**\n",
    "(https://cs224d.stanford.edu/reports/msoh.pdf),(https://arxiv.org/pdf/1707.07998.pdf) and those provided above by udacity.\n",
    "\n",
    "### (Optional) Task #2\n",
    "\n",
    "Note that we have provided a recommended image transform `transform_train` for pre-processing the training images, but you are welcome (and encouraged!) to modify it as you wish.  When modifying this transform, keep in mind that:\n",
    "- the images in the dataset have varying heights and widths, and \n",
    "- if using a pre-trained model, you must perform the corresponding appropriate normalization.\n",
    "\n",
    "### Question 2\n",
    "\n",
    "**Question:** How did you select the transform in `transform_train`?  If you left the transform at its provided value, why do you think that it is a good choice for your CNN architecture?\n",
    "\n",
    "**Answer:** Going through several Research paper, blogs, Code, video lectures and mentor's guidance I conclude that the values for the normalization of the images which are provided by Udacity itself are standard values **and as far as the values of the mean and standard deviation in the transforms.Normalize are concerned, that can't be change. since, the values are default and are considered after rigoruous training of ResNet on the ImageNet Dataset.** \n",
    "\n",
    "### Task #3\n",
    "\n",
    "Next, you will specify a Python list containing the learnable parameters of the model.  For instance, if you decide to make all weights in the decoder trainable, but only want to train the weights in the embedding layer of the encoder, then you should set `params` to something like:\n",
    "```\n",
    "params = list(decoder.parameters()) + list(encoder.embed.parameters()) \n",
    "```\n",
    "\n",
    "### Question 3\n",
    "\n",
    "**Question:** How did you select the trainable parameters of your architecture?  Why do you think this is a good choice?\n",
    "\n",
    "**Answer:** Since, the inital layers of the CNN are already Trained on the ResNet and We have to only use the final part of the CNN feature vector as the input to the decoder so, it's necessary to train the entire weights of the decoder after the embedding layer parameters of the CNN.\n",
    "\n",
    "### Task #4\n",
    "\n",
    "Finally, you will select an [optimizer](http://pytorch.org/docs/master/optim.html#torch.optim.Optimizer).\n",
    "\n",
    "### Question 4\n",
    "\n",
    "**Question:** How did you select the optimizer used to train your model?\n",
    "\n",
    "**Answer:** Till now, After gaining tremendous amount of knowledge and experience here at Udacity as well as from blogs and research and guidance from mentor, I can say that it was all intuitive and going with the instinct after whatever I have learned so far. also, In general Adam optimizer performs better than SGD on numerous occasions. Also, developed a habit of keeping the learning rate low."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vocabulary successfully loaded from vocab.pkl file!\n",
      "loading annotations into memory...\n",
      "Done (t=0.89s)\n",
      "creating index...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  0%|          | 743/414113 [00:00<01:57, 3517.87it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "index created!\n",
      "Obtaining caption lengths...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 414113/414113 [01:36<00:00, 4277.99it/s]\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "from torchvision import transforms\n",
    "import sys\n",
    "sys.path.append('/opt/cocoapi/PythonAPI')\n",
    "from pycocotools.coco import COCO\n",
    "from data_loader import get_loader\n",
    "from model import EncoderCNN, DecoderRNN\n",
    "import math\n",
    "\n",
    "\n",
    "## TODO #1: Select appropriate values for the Python variables below.\n",
    "batch_size = 64          # batch size\n",
    "vocab_threshold = 5        # minimum word count threshold\n",
    "vocab_from_file = True    # if True, load existing vocab file\n",
    "embed_size = 300           # dimensionality of image and word embeddings\n",
    "hidden_size = 512          # number of features in hidden state of the RNN decoder\n",
    "num_epochs = 3             # number of training epochs\n",
    "save_every = 1             # determines frequency of saving model weights\n",
    "print_every = 100          # determines window for printing average loss\n",
    "log_file = 'training_log.txt'       # name of file with saved training loss and perplexity\n",
    "\n",
    "# (Optional) TODO #2: Amend the image transform below.\n",
    "transform_train = transforms.Compose([ \n",
    "    transforms.Resize(256),                          # smaller edge of image resized to 256\n",
    "    transforms.RandomCrop(224),                      # get 224x224 crop from random location\n",
    "    transforms.RandomHorizontalFlip(),               # horizontally flip image with probability=0.5\n",
    "    transforms.ToTensor(),                           # convert the PIL Image to a tensor\n",
    "    transforms.Normalize((0.485, 0.456, 0.406),      # normalize image for pre-trained model\n",
    "                         (0.229, 0.224, 0.225))])\n",
    "\n",
    "# Build data loader.\n",
    "data_loader = get_loader(transform=transform_train,\n",
    "                         mode='train',\n",
    "                         batch_size=batch_size,\n",
    "                         vocab_threshold=vocab_threshold,\n",
    "                         vocab_from_file=vocab_from_file)\n",
    "\n",
    "# The size of the vocabulary.\n",
    "vocab_size = len(data_loader.dataset.vocab)\n",
    "\n",
    "# Initialize the encoder and decoder. \n",
    "encoder = EncoderCNN(embed_size)\n",
    "decoder = DecoderRNN(embed_size, hidden_size, vocab_size)\n",
    "\n",
    "# Move models to GPU if CUDA is available. \n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "encoder.to(device)\n",
    "decoder.to(device)\n",
    "\n",
    "# Define the loss function. \n",
    "criterion = nn.CrossEntropyLoss().cuda() if torch.cuda.is_available() else nn.CrossEntropyLoss()\n",
    "\n",
    "# TODO #3: Specify the learnable parameters of the model.\n",
    "params = list(decoder.parameters()) + list(encoder.embed.parameters())\n",
    "\n",
    "# TODO #4: Define the optimizer.\n",
    "optimizer = torch.optim.Adam(params = params, lr = 0.001)\n",
    "\n",
    "# Set the total number of training steps per epoch.\n",
    "total_step = math.ceil(len(data_loader.dataset.caption_lengths) / data_loader.batch_sampler.batch_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='step2'></a>\n",
    "## Step 2: Train your Model\n",
    "\n",
    "Once you have executed the code cell in **Step 1**, the training procedure below should run without issue.  \n",
    "\n",
    "It is completely fine to leave the code cell below as-is without modifications to train your model.  However, if you would like to modify the code used to train the model below, you must ensure that your changes are easily parsed by your reviewer.  In other words, make sure to provide appropriate comments to describe how your code works!  \n",
    "\n",
    "You may find it useful to load saved weights to resume training.  In that case, note the names of the files containing the encoder and decoder weights that you'd like to load (`encoder_file` and `decoder_file`).  Then you can load the weights by using the lines below:\n",
    "\n",
    "```python\n",
    "# Load pre-trained weights before resuming training.\n",
    "encoder.load_state_dict(torch.load(os.path.join('./models', encoder_file)))\n",
    "decoder.load_state_dict(torch.load(os.path.join('./models', decoder_file)))\n",
    "```\n",
    "\n",
    "While trying out parameters, make sure to take extensive notes and record the settings that you used in your various training runs.  In particular, you don't want to encounter a situation where you've trained a model for several hours but can't remember what settings you used :).\n",
    "\n",
    "### A Note on Tuning Hyperparameters\n",
    "\n",
    "To figure out how well your model is doing, you can look at how the training loss and perplexity evolve during training - and for the purposes of this project, you are encouraged to amend the hyperparameters based on this information.  \n",
    "\n",
    "However, this will not tell you if your model is overfitting to the training data, and, unfortunately, overfitting is a problem that is commonly encountered when training image captioning models.  \n",
    "\n",
    "For this project, you need not worry about overfitting. **This project does not have strict requirements regarding the performance of your model**, and you just need to demonstrate that your model has learned **_something_** when you generate captions on the test data.  For now, we strongly encourage you to train your model for the suggested 3 epochs without worrying about performance; then, you should immediately transition to the next notebook in the sequence (**3_Inference.ipynb**) to see how your model performs on the test data.  If your model needs to be changed, you can come back to this notebook, amend hyperparameters (if necessary), and re-train the model.\n",
    "\n",
    "That said, if you would like to go above and beyond in this project, you can read about some approaches to minimizing overfitting in section 4.3.1 of [this paper](http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=7505636).  In the next (optional) step of this notebook, we provide some guidance for assessing the performance on the validation dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch [1/3], Step [100/6471], Loss: 3.9699, Perplexity: 52.97979\n",
      "Epoch [1/3], Step [200/6471], Loss: 3.5157, Perplexity: 33.6398\n",
      "Epoch [1/3], Step [300/6471], Loss: 3.4962, Perplexity: 32.9908\n",
      "Epoch [1/3], Step [400/6471], Loss: 3.1989, Perplexity: 24.5059\n",
      "Epoch [1/3], Step [500/6471], Loss: 3.0822, Perplexity: 21.8074\n",
      "Epoch [1/3], Step [600/6471], Loss: 2.9842, Perplexity: 19.7708\n",
      "Epoch [1/3], Step [700/6471], Loss: 2.7235, Perplexity: 15.2338\n",
      "Epoch [1/3], Step [800/6471], Loss: 2.8375, Perplexity: 17.0724\n",
      "Epoch [1/3], Step [900/6471], Loss: 2.8182, Perplexity: 16.7468\n",
      "Epoch [1/3], Step [1000/6471], Loss: 2.9086, Perplexity: 18.3313\n",
      "Epoch [1/3], Step [1100/6471], Loss: 2.6927, Perplexity: 14.7711\n",
      "Epoch [1/3], Step [1200/6471], Loss: 2.8404, Perplexity: 17.1232\n",
      "Epoch [1/3], Step [1300/6471], Loss: 2.7810, Perplexity: 16.1350\n",
      "Epoch [1/3], Step [1400/6471], Loss: 2.6727, Perplexity: 14.4793\n",
      "Epoch [1/3], Step [1500/6471], Loss: 2.4218, Perplexity: 11.2665\n",
      "Epoch [1/3], Step [1600/6471], Loss: 2.3561, Perplexity: 10.5499\n",
      "Epoch [1/3], Step [1700/6471], Loss: 2.5789, Perplexity: 13.1823\n",
      "Epoch [1/3], Step [1800/6471], Loss: 2.8376, Perplexity: 17.0754\n",
      "Epoch [1/3], Step [1900/6471], Loss: 2.3996, Perplexity: 11.0185\n",
      "Epoch [1/3], Step [2000/6471], Loss: 2.3246, Perplexity: 10.2228\n",
      "Epoch [1/3], Step [2100/6471], Loss: 2.4718, Perplexity: 11.8438\n",
      "Epoch [1/3], Step [2200/6471], Loss: 2.4758, Perplexity: 11.8916\n",
      "Epoch [1/3], Step [2300/6471], Loss: 2.4685, Perplexity: 11.8048\n",
      "Epoch [1/3], Step [2400/6471], Loss: 2.4409, Perplexity: 11.4831\n",
      "Epoch [1/3], Step [2500/6471], Loss: 2.6980, Perplexity: 14.8494\n",
      "Epoch [1/3], Step [2600/6471], Loss: 2.1003, Perplexity: 8.16893\n",
      "Epoch [1/3], Step [2700/6471], Loss: 2.3713, Perplexity: 10.7109\n",
      "Epoch [1/3], Step [2800/6471], Loss: 2.3317, Perplexity: 10.2956\n",
      "Epoch [1/3], Step [2900/6471], Loss: 2.4582, Perplexity: 11.6843\n",
      "Epoch [1/3], Step [3000/6471], Loss: 2.4510, Perplexity: 11.6001\n",
      "Epoch [1/3], Step [3100/6471], Loss: 2.4259, Perplexity: 11.3125\n",
      "Epoch [1/3], Step [3200/6471], Loss: 2.3297, Perplexity: 10.2744\n",
      "Epoch [1/3], Step [3300/6471], Loss: 2.3916, Perplexity: 10.9312\n",
      "Epoch [1/3], Step [3400/6471], Loss: 2.2580, Perplexity: 9.56428\n",
      "Epoch [1/3], Step [3500/6471], Loss: 2.3253, Perplexity: 10.2302\n",
      "Epoch [1/3], Step [3600/6471], Loss: 2.1947, Perplexity: 8.97712\n",
      "Epoch [1/3], Step [3700/6471], Loss: 2.1784, Perplexity: 8.83190\n",
      "Epoch [1/3], Step [3800/6471], Loss: 2.3439, Perplexity: 10.4223\n",
      "Epoch [1/3], Step [3900/6471], Loss: 2.2733, Perplexity: 9.71128\n",
      "Epoch [1/3], Step [4000/6471], Loss: 2.1601, Perplexity: 8.67236\n",
      "Epoch [1/3], Step [4100/6471], Loss: 2.0748, Perplexity: 7.96288\n",
      "Epoch [1/3], Step [4200/6471], Loss: 2.0402, Perplexity: 7.69228\n",
      "Epoch [1/3], Step [4300/6471], Loss: 3.1429, Perplexity: 23.1717\n",
      "Epoch [1/3], Step [4400/6471], Loss: 2.2871, Perplexity: 9.84601\n",
      "Epoch [1/3], Step [4500/6471], Loss: 2.1769, Perplexity: 8.81909\n",
      "Epoch [1/3], Step [4600/6471], Loss: 2.0541, Perplexity: 7.80027\n",
      "Epoch [1/3], Step [4700/6471], Loss: 2.3102, Perplexity: 10.0765\n",
      "Epoch [1/3], Step [4800/6471], Loss: 2.4618, Perplexity: 11.7258\n",
      "Epoch [1/3], Step [4900/6471], Loss: 2.3096, Perplexity: 10.0701\n",
      "Epoch [1/3], Step [5000/6471], Loss: 2.0836, Perplexity: 8.03345\n",
      "Epoch [1/3], Step [5100/6471], Loss: 2.0892, Perplexity: 8.07860\n",
      "Epoch [1/3], Step [5200/6471], Loss: 2.1234, Perplexity: 8.35973\n",
      "Epoch [1/3], Step [5300/6471], Loss: 2.1445, Perplexity: 8.53821\n",
      "Epoch [1/3], Step [5400/6471], Loss: 2.4847, Perplexity: 11.9970\n",
      "Epoch [1/3], Step [5500/6471], Loss: 2.1268, Perplexity: 8.387746\n",
      "Epoch [1/3], Step [5600/6471], Loss: 2.1016, Perplexity: 8.17969\n",
      "Epoch [1/3], Step [5700/6471], Loss: 2.1818, Perplexity: 8.86244\n",
      "Epoch [1/3], Step [5800/6471], Loss: 2.1736, Perplexity: 8.79013\n",
      "Epoch [1/3], Step [5900/6471], Loss: 1.9738, Perplexity: 7.19786\n",
      "Epoch [1/3], Step [6000/6471], Loss: 1.9862, Perplexity: 7.28774\n",
      "Epoch [1/3], Step [6100/6471], Loss: 2.0884, Perplexity: 8.07203\n",
      "Epoch [1/3], Step [6200/6471], Loss: 2.1188, Perplexity: 8.32093\n",
      "Epoch [1/3], Step [6300/6471], Loss: 2.2275, Perplexity: 9.27694\n",
      "Epoch [1/3], Step [6400/6471], Loss: 2.1280, Perplexity: 8.39800\n",
      "Epoch [2/3], Step [100/6471], Loss: 2.5719, Perplexity: 13.09118\n",
      "Epoch [2/3], Step [200/6471], Loss: 2.0329, Perplexity: 7.63602\n",
      "Epoch [2/3], Step [300/6471], Loss: 2.1332, Perplexity: 8.44173\n",
      "Epoch [2/3], Step [400/6471], Loss: 2.1475, Perplexity: 8.56334\n",
      "Epoch [2/3], Step [500/6471], Loss: 2.1518, Perplexity: 8.60012\n",
      "Epoch [2/3], Step [600/6471], Loss: 2.4147, Perplexity: 11.1865\n",
      "Epoch [2/3], Step [700/6471], Loss: 2.2660, Perplexity: 9.64049\n",
      "Epoch [2/3], Step [800/6471], Loss: 1.9848, Perplexity: 7.27775\n",
      "Epoch [2/3], Step [900/6471], Loss: 1.9973, Perplexity: 7.36914\n",
      "Epoch [2/3], Step [1000/6471], Loss: 2.0720, Perplexity: 7.9404\n",
      "Epoch [2/3], Step [1100/6471], Loss: 2.1927, Perplexity: 8.95951\n",
      "Epoch [2/3], Step [1200/6471], Loss: 2.2016, Perplexity: 9.03980\n",
      "Epoch [2/3], Step [1300/6471], Loss: 2.1173, Perplexity: 8.30864\n",
      "Epoch [2/3], Step [1400/6471], Loss: 1.9638, Perplexity: 7.12605\n",
      "Epoch [2/3], Step [1500/6471], Loss: 2.1328, Perplexity: 8.43827\n",
      "Epoch [2/3], Step [1600/6471], Loss: 2.5358, Perplexity: 12.6259\n",
      "Epoch [2/3], Step [1700/6471], Loss: 2.3993, Perplexity: 11.0153\n",
      "Epoch [2/3], Step [1800/6471], Loss: 2.2180, Perplexity: 9.18867\n",
      "Epoch [2/3], Step [1900/6471], Loss: 2.4407, Perplexity: 11.4813\n",
      "Epoch [2/3], Step [2000/6471], Loss: 2.2160, Perplexity: 9.17040\n",
      "Epoch [2/3], Step [2100/6471], Loss: 1.9779, Perplexity: 7.22775\n",
      "Epoch [2/3], Step [2200/6471], Loss: 2.0243, Perplexity: 7.57098\n",
      "Epoch [2/3], Step [2300/6471], Loss: 2.1773, Perplexity: 8.82226\n",
      "Epoch [2/3], Step [2400/6471], Loss: 2.1241, Perplexity: 8.36511\n",
      "Epoch [2/3], Step [2500/6471], Loss: 2.3414, Perplexity: 10.3955\n",
      "Epoch [2/3], Step [2600/6471], Loss: 1.8351, Perplexity: 6.26598\n",
      "Epoch [2/3], Step [2700/6471], Loss: 2.1143, Perplexity: 8.28367\n",
      "Epoch [2/3], Step [2800/6471], Loss: 2.1918, Perplexity: 8.95132\n",
      "Epoch [2/3], Step [2900/6471], Loss: 2.0471, Perplexity: 7.74578\n",
      "Epoch [2/3], Step [3000/6471], Loss: 1.8681, Perplexity: 6.47619\n",
      "Epoch [2/3], Step [3100/6471], Loss: 2.0069, Perplexity: 7.44027\n",
      "Epoch [2/3], Step [3200/6471], Loss: 2.0977, Perplexity: 8.14773\n",
      "Epoch [2/3], Step [3300/6471], Loss: 2.0484, Perplexity: 7.75577\n",
      "Epoch [2/3], Step [3400/6471], Loss: 1.9805, Perplexity: 7.24639\n",
      "Epoch [2/3], Step [3500/6471], Loss: 2.1656, Perplexity: 8.72004\n",
      "Epoch [2/3], Step [3600/6471], Loss: 2.8932, Perplexity: 18.0512\n",
      "Epoch [2/3], Step [3700/6471], Loss: 1.9891, Perplexity: 7.30872\n",
      "Epoch [2/3], Step [3800/6471], Loss: 2.0752, Perplexity: 7.96647\n",
      "Epoch [2/3], Step [3900/6471], Loss: 1.8505, Perplexity: 6.36306\n",
      "Epoch [2/3], Step [4000/6471], Loss: 1.7454, Perplexity: 5.72844\n",
      "Epoch [2/3], Step [4100/6471], Loss: 1.9726, Perplexity: 7.18964\n",
      "Epoch [2/3], Step [4200/6471], Loss: 1.8828, Perplexity: 6.57190\n",
      "Epoch [2/3], Step [4300/6471], Loss: 2.3961, Perplexity: 10.9801\n",
      "Epoch [2/3], Step [4400/6471], Loss: 2.2191, Perplexity: 9.19907\n",
      "Epoch [2/3], Step [4500/6471], Loss: 1.9156, Perplexity: 6.79118\n",
      "Epoch [2/3], Step [4600/6471], Loss: 2.2147, Perplexity: 9.15837\n",
      "Epoch [2/3], Step [4700/6471], Loss: 2.1959, Perplexity: 8.98833\n",
      "Epoch [2/3], Step [4800/6471], Loss: 2.1183, Perplexity: 8.31680\n",
      "Epoch [2/3], Step [4900/6471], Loss: 2.0099, Perplexity: 7.46236\n",
      "Epoch [2/3], Step [5000/6471], Loss: 2.1431, Perplexity: 8.52571\n",
      "Epoch [2/3], Step [5100/6471], Loss: 1.8999, Perplexity: 6.68545\n",
      "Epoch [2/3], Step [5200/6471], Loss: 1.8732, Perplexity: 6.50904\n",
      "Epoch [2/3], Step [5300/6471], Loss: 2.2680, Perplexity: 9.65979\n",
      "Epoch [2/3], Step [5400/6471], Loss: 1.8445, Perplexity: 6.32497\n",
      "Epoch [2/3], Step [5500/6471], Loss: 1.9962, Perplexity: 7.36144\n",
      "Epoch [2/3], Step [5600/6471], Loss: 1.9387, Perplexity: 6.94982\n",
      "Epoch [2/3], Step [5700/6471], Loss: 2.3956, Perplexity: 10.9745\n",
      "Epoch [2/3], Step [5800/6471], Loss: 1.8345, Perplexity: 6.26193\n",
      "Epoch [2/3], Step [5900/6471], Loss: 2.2035, Perplexity: 9.05643\n",
      "Epoch [2/3], Step [6000/6471], Loss: 2.2707, Perplexity: 9.68641\n",
      "Epoch [2/3], Step [6100/6471], Loss: 2.1088, Perplexity: 8.23853\n",
      "Epoch [2/3], Step [6200/6471], Loss: 2.1520, Perplexity: 8.60200\n",
      "Epoch [2/3], Step [6300/6471], Loss: 2.0779, Perplexity: 7.98778\n",
      "Epoch [2/3], Step [6400/6471], Loss: 2.6915, Perplexity: 14.7536\n",
      "Epoch [3/3], Step [100/6471], Loss: 2.0959, Perplexity: 8.132657\n",
      "Epoch [3/3], Step [200/6471], Loss: 2.2041, Perplexity: 9.06239\n",
      "Epoch [3/3], Step [300/6471], Loss: 2.0582, Perplexity: 7.83177\n",
      "Epoch [3/3], Step [400/6471], Loss: 2.1110, Perplexity: 8.25664\n",
      "Epoch [3/3], Step [500/6471], Loss: 1.8621, Perplexity: 6.43704\n",
      "Epoch [3/3], Step [600/6471], Loss: 2.1389, Perplexity: 8.48971\n",
      "Epoch [3/3], Step [700/6471], Loss: 1.9460, Perplexity: 7.00098\n",
      "Epoch [3/3], Step [800/6471], Loss: 2.1262, Perplexity: 8.38263\n",
      "Epoch [3/3], Step [900/6471], Loss: 2.1721, Perplexity: 8.77631\n",
      "Epoch [3/3], Step [1000/6471], Loss: 1.8851, Perplexity: 6.5872\n",
      "Epoch [3/3], Step [1100/6471], Loss: 1.8455, Perplexity: 6.33125\n",
      "Epoch [3/3], Step [1200/6471], Loss: 1.8103, Perplexity: 6.11230\n",
      "Epoch [3/3], Step [1300/6471], Loss: 2.0143, Perplexity: 7.49551\n",
      "Epoch [3/3], Step [1400/6471], Loss: 1.8937, Perplexity: 6.64373\n",
      "Epoch [3/3], Step [1500/6471], Loss: 1.8013, Perplexity: 6.05766\n",
      "Epoch [3/3], Step [1600/6471], Loss: 2.0285, Perplexity: 7.60308\n",
      "Epoch [3/3], Step [1700/6471], Loss: 2.0932, Perplexity: 8.11082\n",
      "Epoch [3/3], Step [1800/6471], Loss: 1.9103, Perplexity: 6.75491\n",
      "Epoch [3/3], Step [1900/6471], Loss: 1.9410, Perplexity: 6.96596\n",
      "Epoch [3/3], Step [2000/6471], Loss: 2.0903, Perplexity: 8.08741\n",
      "Epoch [3/3], Step [2100/6471], Loss: 1.8796, Perplexity: 6.55108\n",
      "Epoch [3/3], Step [2200/6471], Loss: 2.1285, Perplexity: 8.40269\n",
      "Epoch [3/3], Step [2300/6471], Loss: 1.9398, Perplexity: 6.95700\n",
      "Epoch [3/3], Step [2400/6471], Loss: 1.9647, Perplexity: 7.13274\n",
      "Epoch [3/3], Step [2500/6471], Loss: 2.6071, Perplexity: 13.5594\n",
      "Epoch [3/3], Step [2600/6471], Loss: 1.8909, Perplexity: 6.62557\n",
      "Epoch [3/3], Step [2700/6471], Loss: 1.8007, Perplexity: 6.05396\n",
      "Epoch [3/3], Step [2800/6471], Loss: 2.1203, Perplexity: 8.33394\n",
      "Epoch [3/3], Step [2900/6471], Loss: 1.8401, Perplexity: 6.29704\n",
      "Epoch [3/3], Step [3000/6471], Loss: 1.9672, Perplexity: 7.15070\n",
      "Epoch [3/3], Step [3100/6471], Loss: 1.8342, Perplexity: 6.26003\n",
      "Epoch [3/3], Step [3200/6471], Loss: 1.9892, Perplexity: 7.30965\n",
      "Epoch [3/3], Step [3300/6471], Loss: 2.0163, Perplexity: 7.51019\n",
      "Epoch [3/3], Step [3400/6471], Loss: 2.1896, Perplexity: 8.93139\n",
      "Epoch [3/3], Step [3500/6471], Loss: 1.9497, Perplexity: 7.02650\n",
      "Epoch [3/3], Step [3600/6471], Loss: 2.1679, Perplexity: 8.73985\n",
      "Epoch [3/3], Step [3700/6471], Loss: 1.9430, Perplexity: 6.97974\n",
      "Epoch [3/3], Step [3800/6471], Loss: 2.1147, Perplexity: 8.28754\n",
      "Epoch [3/3], Step [3900/6471], Loss: 1.8213, Perplexity: 6.18006\n",
      "Epoch [3/3], Step [4000/6471], Loss: 2.0052, Perplexity: 7.42741\n",
      "Epoch [3/3], Step [4100/6471], Loss: 2.0823, Perplexity: 8.02282\n",
      "Epoch [3/3], Step [4200/6471], Loss: 1.9440, Perplexity: 6.98668\n",
      "Epoch [3/3], Step [4300/6471], Loss: 2.1064, Perplexity: 8.21877\n",
      "Epoch [3/3], Step [4400/6471], Loss: 1.7790, Perplexity: 5.92402\n",
      "Epoch [3/3], Step [4500/6471], Loss: 1.8528, Perplexity: 6.37792\n",
      "Epoch [3/3], Step [4600/6471], Loss: 2.0854, Perplexity: 8.04798\n",
      "Epoch [3/3], Step [4700/6471], Loss: 1.8109, Perplexity: 6.11628\n",
      "Epoch [3/3], Step [4800/6471], Loss: 1.9043, Perplexity: 6.71450\n",
      "Epoch [3/3], Step [4900/6471], Loss: 1.8752, Perplexity: 6.52218\n",
      "Epoch [3/3], Step [5000/6471], Loss: 3.0823, Perplexity: 21.8081\n",
      "Epoch [3/3], Step [5100/6471], Loss: 1.9743, Perplexity: 7.20142\n",
      "Epoch [3/3], Step [5200/6471], Loss: 1.9712, Perplexity: 7.17940\n",
      "Epoch [3/3], Step [5300/6471], Loss: 1.7921, Perplexity: 6.00208\n",
      "Epoch [3/3], Step [5400/6471], Loss: 1.8750, Perplexity: 6.52053\n",
      "Epoch [3/3], Step [5500/6471], Loss: 2.0119, Perplexity: 7.47753\n",
      "Epoch [3/3], Step [5600/6471], Loss: 1.9627, Perplexity: 7.11868\n",
      "Epoch [3/3], Step [5700/6471], Loss: 1.9784, Perplexity: 7.23102\n",
      "Epoch [3/3], Step [5800/6471], Loss: 1.9243, Perplexity: 6.85034\n",
      "Epoch [3/3], Step [5900/6471], Loss: 2.0358, Perplexity: 7.65820\n",
      "Epoch [3/3], Step [6000/6471], Loss: 2.1127, Perplexity: 8.27080\n",
      "Epoch [3/3], Step [6100/6471], Loss: 2.1856, Perplexity: 8.89606\n",
      "Epoch [3/3], Step [6200/6471], Loss: 1.8927, Perplexity: 6.63715\n",
      "Epoch [3/3], Step [6300/6471], Loss: 1.9605, Perplexity: 7.10310\n",
      "Epoch [3/3], Step [6400/6471], Loss: 1.8988, Perplexity: 6.67766\n",
      "Epoch [3/3], Step [6471/6471], Loss: 2.0848, Perplexity: 8.04306"
     ]
    }
   ],
   "source": [
    "import torch.utils.data as data\n",
    "import numpy as np\n",
    "import os\n",
    "import requests\n",
    "import time\n",
    "\n",
    "# Open the training log file.\n",
    "f = open(log_file, 'w')\n",
    "\n",
    "old_time = time.time()\n",
    "response = requests.request(\"GET\", \n",
    "                            \"http://metadata.google.internal/computeMetadata/v1/instance/attributes/keep_alive_token\", \n",
    "                            headers={\"Metadata-Flavor\":\"Google\"})\n",
    "\n",
    "for epoch in range(1, num_epochs+1):\n",
    "    \n",
    "    for i_step in range(1, total_step+1):\n",
    "        \n",
    "        if time.time() - old_time > 60:\n",
    "            old_time = time.time()\n",
    "            requests.request(\"POST\", \n",
    "                             \"https://nebula.udacity.com/api/v1/remote/keep-alive\", \n",
    "                             headers={'Authorization': \"STAR \" + response.text})\n",
    "        \n",
    "        # Randomly sample a caption length, and sample indices with that length.\n",
    "        indices = data_loader.dataset.get_train_indices()\n",
    "        # Create and assign a batch sampler to retrieve a batch with the sampled indices.\n",
    "        new_sampler = data.sampler.SubsetRandomSampler(indices=indices)\n",
    "        data_loader.batch_sampler.sampler = new_sampler\n",
    "        \n",
    "        # Obtain the batch.\n",
    "        images, captions = next(iter(data_loader))\n",
    "\n",
    "        # Move batch of images and captions to GPU if CUDA is available.\n",
    "        images = images.to(device)\n",
    "        captions = captions.to(device)\n",
    "        \n",
    "        # Zero the gradients.\n",
    "        decoder.zero_grad()\n",
    "        encoder.zero_grad()\n",
    "        \n",
    "        # Pass the inputs through the CNN-RNN model.\n",
    "        features = encoder(images)\n",
    "        outputs = decoder(features, captions)\n",
    "        \n",
    "        # Calculate the batch loss.\n",
    "        loss = criterion(outputs.view(-1, vocab_size), captions.view(-1))\n",
    "        \n",
    "        # Backward pass.\n",
    "        loss.backward()\n",
    "        \n",
    "        # Update the parameters in the optimizer.\n",
    "        optimizer.step()\n",
    "            \n",
    "        # Get training statistics.\n",
    "        stats = 'Epoch [%d/%d], Step [%d/%d], Loss: %.4f, Perplexity: %5.4f' % (epoch, num_epochs, i_step, total_step, loss.item(), np.exp(loss.item()))\n",
    "        \n",
    "        # Print training statistics (on same line).\n",
    "        print('\\r' + stats, end=\"\")\n",
    "        sys.stdout.flush()\n",
    "        \n",
    "        # Print training statistics to file.\n",
    "        f.write(stats + '\\n')\n",
    "        f.flush()\n",
    "        \n",
    "        # Print training statistics (on different line).\n",
    "        if i_step % print_every == 0:\n",
    "            print('\\r' + stats)\n",
    "            \n",
    "    # Save the weights.\n",
    "    if epoch % save_every == 0:\n",
    "        torch.save(decoder.state_dict(), os.path.join('./models', 'decoder-%d.pkl' % epoch))\n",
    "        torch.save(encoder.state_dict(), os.path.join('./models', 'encoder-%d.pkl' % epoch))\n",
    "\n",
    "# Close the training log file.\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='step3'></a>\n",
    "## Step 3: (Optional) Validate your Model\n",
    "\n",
    "To assess potential overfitting, one approach is to assess performance on a validation set.  If you decide to do this **optional** task, you are required to first complete all of the steps in the next notebook in the sequence (**3_Inference.ipynb**); as part of that notebook, you will write and test code (specifically, the `sample` method in the `DecoderRNN` class) that uses your RNN decoder to generate captions.  That code will prove incredibly useful here. \n",
    "\n",
    "If you decide to validate your model, please do not edit the data loader in **data_loader.py**.  Instead, create a new file named **data_loader_val.py** containing the code for obtaining the data loader for the validation data.  You can access:\n",
    "- the validation images at filepath `'/opt/cocoapi/images/train2014/'`, and\n",
    "- the validation image caption annotation file at filepath `'/opt/cocoapi/annotations/captions_val2014.json'`.\n",
    "\n",
    "The suggested approach to validating your model involves creating a json file such as [this one](https://github.com/cocodataset/cocoapi/blob/master/results/captions_val2014_fakecap_results.json) containing your model's predicted captions for the validation images.  Then, you can write your own script or use one that you [find online](https://github.com/tylin/coco-caption) to calculate the BLEU score of your model.  You can read more about the BLEU score, along with other evaluation metrics (such as TEOR and Cider) in section 4.1 of [this paper](https://arxiv.org/pdf/1411.4555.pdf).  For more information about how to use the annotation file, check out the [website](http://cocodataset.org/#download) for the COCO dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# (Optional) TODO: Validate your model."
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
